Passed
Pull Request — filestream (#169)
by
unknown
04:52
created

file-write.ts ➔ getTmpFilePathSync   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
1
import {
2
    fsWritePromise,
3
    fillBufferAsync,
4
    fillBufferSync,
5
    processFileSync,
6
    processFileAsync
7
} from "./util-file"
8
import * as fs from 'fs'
9
import { findId3TagPosition, getId3TagSize } from "./id3-tag"
10
import { WriteCallback } from "./types/write"
11
12
const FileBufferSize = 20 * 1024 * 1024
13
14
export function writeId3TagToFileSync(filepath: string, id3Tag: Buffer) {
15
    if (!fs.existsSync(filepath)) {
16
        processFileSync(filepath, "w", (fileDescriptor) =>
17
            fs.writeSync(fileDescriptor, id3Tag)
18
        )
19
        return
20
    }
21
    const tempFilepath = makeTempFilepath(filepath)
22
    processFileSync(filepath, 'r', (readFileDescriptor) => {
23
        try {
24
            processFileSync(tempFilepath, 'w', (writeFileDescriptor) => {
25
                fs.writeSync(writeFileDescriptor, id3Tag)
26
                copyFileWithoutId3TagSync(readFileDescriptor, writeFileDescriptor)
27
            })
28
        } catch(error) {
29
            fs.unlinkSync(tempFilepath)
30
            throw error
31
        }
32
    })
33
    fs.renameSync(tempFilepath, filepath)
34
}
35
36
// TODO handle the case when there is no source file and create a new one.
37
export function writeId3TagToFileAsync(
38
    filepath: string,
39
    id3Tag: Buffer,
40
    callback: WriteCallback
41
) {
42
    const tempFilepath = makeTempFilepath(filepath)
43
    processFileAsync(filepath, 'r', async (readFileDescriptor) => {
44
        processFileAsync(tempFilepath, 'w', async (writeFileDescriptor) => {
45
            await fsWritePromise(writeFileDescriptor, id3Tag)
46
            await copyFileWithoutId3TagAsync(readFileDescriptor, writeFileDescriptor)
47
        }).catch((error) => {
48
            fs.unlink(tempFilepath, callback)
49
            throw error
50
        })
51
    }).then(() => {
52
        fs.rename(tempFilepath, filepath, callback)
53
    }).catch(callback)
54
}
55
56
function makeTempFilepath(filepath: string) {
57
    return `${filepath}.tmp-${Date.now()}`
58
}
59
60
function copyFileWithoutId3TagSync(
61
    readFileDescriptor: number,
62
    writeFileDescriptor: number
63
) {
64
    const buffer = Buffer.alloc(FileBufferSize)
65
    let readData
66
    while((readData = fillBufferSync(readFileDescriptor, buffer)).length) {
67
        const { data, bytesToSkip } = removeId3TagIfFound(readData)
68
        if (bytesToSkip) {
69
            fillBufferSync(readFileDescriptor, Buffer.alloc(bytesToSkip))
70
        }
71
        fs.writeSync(writeFileDescriptor, data, 0, data.length, null)
72
    }
73
}
74
75
async function copyFileWithoutId3TagAsync(
76
    readFileDescriptor: number,
77
    writeFileDescriptor: number
78
) {
79
    const buffer = Buffer.alloc(FileBufferSize)
80
    let readData
81
    while((readData = await fillBufferAsync(readFileDescriptor, buffer)).length) {
82
        const { data, bytesToSkip } = removeId3TagIfFound(readData)
83
        if (bytesToSkip) {
84
            await fillBufferAsync(readFileDescriptor, Buffer.alloc(bytesToSkip))
85
        }
86
        await fsWritePromise(writeFileDescriptor, data, 0, data.length, null)
87
    }
88
}
89
90
function removeId3TagIfFound(data: Buffer) {
91
    const id3TagPosition = findId3TagPosition(data)
92
    if (id3TagPosition === -1) {
93
        return { data }
94
    }
95
    const dataFromId3Start = data.subarray(id3TagPosition)
96
    const id3TagSize = getId3TagSize(dataFromId3Start)
97
    return {
98
        data: Buffer.concat([
99
            data.subarray(0, id3TagPosition),
100
            dataFromId3Start.subarray(Math.min(id3TagSize, dataFromId3Start.length))
101
        ]),
102
        bytesToSkip: Math.max(0, id3TagSize - dataFromId3Start.length)
103
    }
104
}
105